import axios from 'axios'
import Worker from './hash.worker.js'
export class UploadManager {
    chunkSize = 1 * 1024 * 1024
    constructor(options, handlers) {
        this.options = options
        this.progress = null;
        this.transProgress = null;
        this.isStop = false
        this.file = options.file;
        this.bucketName = options.bucketName;
        this.objectName = options.objectName;
        this.storage = options.storage;
        this.fileInfo = null;
        this.aborted = false;
        this.remainChunks = null;
        this.uploadUrl = this.storage.ApiUrl;
        this.onData = () => { };
        this.onError = () => { };
        this.onComplete = () => { };
        Object.assign(this, handlers);
    }

    putFile() {
        let upload = () => {
            // 如果文件大小大于文件分片大小的5倍才使用分片上传
            return this.file.size / this.chunkSize >= 5 ? this.resumeUpload() : this.directUpload();
        }
        upload().then(res => {
            this.onComplete(res)
        }).catch(err => {
            this.onError(err);
        })
    }

    //分片
    async resumeUpload() {
        return this.createFileMd5(this.file).then(async fileMd5 => {
            // 先查询服务器是否已有上传完的文件切片
            let { data } = await this.getUploadedChunks(fileMd5)
            let uploaded = data.data.length ? data.data.map(v => v.split('_')[1] - 0) : []
            // 切割文件
            const chunkArr = await this.cutBlob(fileMd5, this.file, uploaded)
            console.log(chunkArr)
            // 开始上传
            return this.sendRequest(chunkArr, 6).then(() => this.chunkMerge())
        })
    }
    createFileMd5(file) {
        return new Promise((resolve) => {
            const worker = new Worker()
            worker.postMessage({ file, chunkSize: this.chunkSize })
            worker.onmessage = event => {
                resolve(event.data)
            }
        })
    }
    // 请求已上传文件
    getUploadedChunks(hash) {
        return axios.post(this.uploadUrl + 'object/upload/checkSnippet', { hash })
    }
    // 文件分割
    cutBlob(fileHash, file, uploaded) {
        const chunkArr = [] // 所有切片缓存数组
        const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
        const chunkNums = Math.ceil(file.size / this.chunkSize) // 切片总数

        return new Promise(resolve => {
            let startIndex = ''
            let endIndex = ''
            let contentItem = ''

            for (let i = 0; i < chunkNums; i++) {
                // 如果已上传则跳过
                if (uploaded.includes(i)) {
                    console.log("跳过")
                    continue
                }

                startIndex = i * this.chunkSize // 片段起点
                endIndex = (i + 1) * this.chunkSize // 片段尾点
                endIndex > file.size && (endIndex = file.size)

                // 切割文件
                contentItem = blobSlice.call(file, startIndex, endIndex)

                chunkArr.push({
                    index: i,
                    chunk: contentItem
                })
            }
            this.fileInfo = {
                hash: fileHash,
                total: chunkNums,
                name: file.name,
                size: file.size,
                loaded: (uploaded.length / chunkNums) * file.size
            }
            resolve(chunkArr)
        })
    }
    sendChunk(item) {
        const formData = new FormData()
        formData.append("file", item.chunk)
        formData.append("hash", this.fileInfo.hash)
        formData.append("index", item.index)
        formData.append("name", this.fileInfo.name)
        const configs = {
            headers: { 'Content-Type': 'multipart/form-data' },
            onUploadProgress: (data) => {
                this.fileInfo.loaded += data.loaded
                this.updateDirectProgress(this.fileInfo.loaded, this.fileInfo.size);
            },
        }
        return axios.post(this.uploadUrl + 'object/upload/snippet', formData, configs)
    }
    async chunkMerge() {
        return axios.post(this.uploadUrl + 'object/upload/merge', { ...this.fileInfo, bucketName: this.bucketName, objectName: this.objectName }).then(res => {
            return res.data.data
        }).catch(err => {
            this.clear();
            return err
        })
    }
    sendRequest(arr, max = 6) {
        let fetchArr = []
        let toFetch = () => {
            if (this.isStop) {
                return Promise.reject('暂停上传')
            }
            if (!arr.length) {
                return Promise.resolve()
            }
            const chunkItem = arr.shift()
            const it = this.sendChunk(chunkItem)
            it.then(() => {
                fetchArr.splice(fetchArr.indexOf(it), 1)
            }, err => {
                this.isStop = true
                arr.unshift(chunkItem)
                Promise.reject(err)
            })
            fetchArr.push(it)

            let p = Promise.resolve()
            if (fetchArr.length >= max) {
                p = Promise.race(fetchArr)
            }

            return p.then(() => toFetch())
        }

        return toFetch().then(() => {
            return Promise.all(fetchArr)
        }).catch(err => {
            return err
        })
    }
    //直传
    async directUpload() {
        const formData = new FormData();
        formData.append('file', this.file);
        formData.append('objectName', this.objectName);
        formData.append('bucketName', this.bucketName);
        const configs = {
            headers: { 'Content-Type': 'multipart/form-data' },
            onUploadProgress: (data) => {
                this.updateDirectProgress(data.loaded, data.total);
            },
        }
        return axios.post(this.uploadUrl + 'object/upload', formData, configs).then(res => {
            return res.data.data
        }).catch(err => {
            this.clear();
            return err
        })
    }
    clear() {
        this.fileInfo = null;
        this.remainChunks = null;
    }
    stop() {
        this.clear();
        this.aborted = true;
    }
    updateDirectProgress(loaded, total) {
        // 当请求未完成时可能进度会达到100，所以total + 1来防止这种情况出现
        this.progress = { total: this.getProgressInfoItem(loaded, total + 1) };
        this.onData(this.progress);
    }
    updateDirectTransProgress(progress) {
        this.transProgress = progress;
        this.onData(transProgress);
    }

    getProgressInfoItem(loaded, size) {
        return {
            loaded: loaded,
            size: size,
            percent: (loaded / size * 100) >= 100 ? 100 : (loaded / size * 100)
        };
    }
}